using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Input;

namespace FIFP.Common
{

    /// <summary>
    /// </summary>
    public sealed class RelayCommand : ICommand, ICommandModifier
    {
        private readonly Action<object> _execute;
        private readonly Func<object, bool> _canExecute;
        private readonly object _commandParameter;
        
        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand"/> class.
        /// </summary>
        /// <param name="execute">The execute.</param>
        public RelayCommand(Action<object> execute)
            : this(execute, null, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand"/> class.
        /// </summary>
        /// <param name="execute">The execute.</param>
        /// <param name="commandParameter">The command parameter.</param>
        public RelayCommand(Action<object> execute, object commandParameter)
            : this(execute, null, commandParameter)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand"/> class.
        /// </summary>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
            : this(execute, canExecute, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand"/> class.
        /// </summary>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        /// <param name="commandParameter">The command parameter.</param>
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute, object commandParameter)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
            _commandParameter = commandParameter;
        }

        /// <summary>
        /// Gets the command parameter.
        /// </summary>
        /// <value>The command parameter.</value>
        public object CommandParameter {
            get { return _commandParameter; }
        }

        /// <summary>
        /// Defines the method that determines whether the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        /// <returns>
        /// true if this command can be executed; otherwise, false.
        /// </returns>
        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            //if no parameter is passed in automatically, determine if one has been set on the Command object itself.
            //If it has not, it will have a default value of null.   
            var param = parameter ?? _commandParameter;
            return _canExecute == null ? true : _canExecute(param);
        }

        /// <summary>
        /// Raises the <see cref="CanExecuteChanged" /> event.
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
            Justification = "The this keyword is used in the Silverlight version")]
        [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate",
            Justification = "This cannot be an event")]
        public void RaiseCanExecuteChanged() {
            var handler = CanExecuteChanged;
            if(handler != null) {
                handler(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Occurs when changes occur that affect whether or not the command should execute.
        /// </summary>
        /// <summary>
        /// Occurs when changes occur that affect whether the command should execute.
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        public void Execute(object parameter)
        {
            //if no parameter is passed in automatically, determine if one has been set on the Command object itself.
            //If it has not, it will have a default value of null.   
            var param = parameter ?? _commandParameter;
            _execute(param);
        }

        public ModifierKeys Modifier { get; set; }

        /// <summary>
        /// Creates a <see cref="RelayCommand"/> using the specifed Action delegate on executing and taking a <see cref="Func{T,TResult}"/>
        /// delegate to determine whether the command is allowed to execute.
        /// </summary>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        /// <returns></returns>
        public static RelayCommand GetCommand(Action<object> execute, Func<object, bool> canExecute) {
            return new RelayCommand(execute, canExecute);
        }


        /// <summary>
        /// Creates a <see cref="RelayCommand"/> using the specifed Action delegate on executing.
        /// </summary>
        /// <param name="execute">The execute.</param>
        /// <returns></returns>
        public static RelayCommand GetCommand(Action<object> execute) {
            return new RelayCommand(execute);
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public sealed class RelayCommand<T> : ICommand, ICommandModifier {

        private readonly Action<T> _execute;
        private readonly Func<T, bool> _canExecute;
        private readonly T _commandParameter;

        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="execute">The execute.</param>
        public RelayCommand(Action<T> execute)
            : this(execute, null, default(T))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="execute">The execute.</param>
        /// <param name="commandParameter">The command parameter.</param>
        public RelayCommand(Action<T> execute, T commandParameter)
            : this(execute, null, commandParameter)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        public RelayCommand(Action<T> execute, Func<T, bool> canExecute) 
            : this(execute,canExecute, default(T))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        /// <param name="commandParameter">The command parameter.</param>
        public RelayCommand(Action<T> execute, Func<T, bool> canExecute, T commandParameter)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
            _commandParameter = commandParameter;
        }

        /// <summary>
        /// Gets the command parameter.
        /// </summary>
        /// <value>The command parameter.</value>
        public T CommandParameter
        {
            get { return _commandParameter; }
        }

        /// <summary>
        /// Defines the method that determines whether the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        /// <returns>
        /// true if this command can be executed; otherwise, false.
        /// </returns>
        [DebuggerStepThrough]
        public bool CanExecute(T parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        /// <summary>
        /// Raises the <see cref="CanExecuteChanged" /> event.
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
            Justification = "The this keyword is used in the Silverlight version")]
        [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate",
            Justification = "This cannot be an event")]
        public void RaiseCanExecuteChanged() {
            var handler = CanExecuteChanged;
            if(handler != null) {
                handler(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Occurs when changes occur that affect whether or not the command should execute.
        /// </summary>
        /// <summary>
        /// Occurs when changes occur that affect whether the command should execute.
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        public void Execute(T parameter)
        {
            _execute(parameter);
        }

        ///<summary>
        ///Defines the method that determines whether the command can execute in its current state.
        ///</summary>
        ///<param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        ///<returns>
        ///true if this command can be executed; otherwise, false.
        ///</returns>
        bool ICommand.CanExecute(object parameter)
        {
            //if no parameter is passed in automatically, determine if one has been set on the Command object itself.
            //If it has not, it will have a default value of default(T).   
            var typed = parameter == null ? _commandParameter : (T)parameter;
            return CanExecute(typed);
        }

        ///<summary>
        ///Defines the method to be called when the command is invoked.
        ///</summary>
        ///<param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        void ICommand.Execute(object parameter)
        {
            //if no parameter is passed in automatically, determine if one has been set on the Command object itself.
            //If it has not, it will have a default value of default(T).   
            var typed = parameter == null ? _commandParameter : (T)parameter;
            Execute(typed);
        }

        public ModifierKeys Modifier { get; set; }

        /// <summary>
        /// Creates a <see cref="RelayCommand{TParam}"/> using the specifed <see cref="Action{T}"/> delegate on executing and taking a <see cref="Func{T,TResult}"/>
        /// delegate to determine whether the command is allowed to execute.
        /// </summary>
        /// <typeparam name="TParam">The type of the param.</typeparam>
        /// <param name="value">The value.</param>
        /// <param name="execute">The execute.</param>
        /// <param name="canExecute">The can execute.</param>
        /// <returns></returns>
        public static RelayCommand<TParam> GetCommand<TParam>(TParam value, Action<TParam> execute, Func<TParam, bool> canExecute) {
            return new RelayCommand<TParam>(execute, canExecute, value);
        }

        /// <summary>
        /// Creates a <see cref="RelayCommand{TParam}"/> using the specifed <see cref="Action{T}"/> delegate on executing.
        /// </summary>
        /// <typeparam name="TParam">The type of the param.</typeparam>
        /// <param name="value">The value.</param>
        /// <param name="execute">The execute.</param>
        /// <returns></returns>
        public static RelayCommand<TParam> GetCommand<TParam>(TParam value, Action<TParam> execute) {
            return new RelayCommand<TParam>(execute, value);
        }
    }
}